#include "stdafx.h"
#include "ScriptBase.h"
#include <BRepMesh_IncrementalMesh.hxx>
#include <TopExp_Explorer.hxx>

#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Shell.hxx>
#include <Poly_Triangulation.hxx>
#include <TShort_Array1OfShortReal.hxx>
//#include <StdPrs_ToolShadedShape.hxx>
#include <Poly_Connect.hxx>
#include <BRepTools.hxx>
#include <TColgp_Array1OfDir.hxx>
#include <TColgp_Array1OfPnt2d.hxx>


ScriptBase::ScriptBase(ZMString strObjName):MeshObjectBase(strObjName)
{

}


ScriptBase::~ScriptBase()
{
}

int ScriptBase::TriangleShapeWithOcc(
	const TopoDS_Shape& shape, 
	ZMVector<ZMPoint3D>& vPnt,
	ZMVector<ZMPoint2D>& vUV, 
	ZMVector<ZMPoint3D>& vNormal, 
	ZMVector<ZMPoint3D>& vTangentData, 
	ZMVector<int>& vTriIndex,
	const ZMPoint2D theUVOrigin,
	const ZMPoint2D theUVRepeat,
	const ZMPoint2D theUVScale
)
{
	BRepMesh_IncrementalMesh(shape, 1);

	TopLoc_Location aLoc;
	gp_Pnt aPoint;
	Standard_Integer aNbTriangles = 0;
	Standard_Integer aNbVertices = 0;

	// Precision for compare square distances
	const Standard_Real aPreci = Precision::SquareConfusion();

	for (TopExp_Explorer aFaceIt(shape, TopAbs_FACE); aFaceIt.More(); aFaceIt.Next())
	{
		const TopoDS_Face& aFace = TopoDS::Face(aFaceIt.Current());
		//Handle(Poly_Triangulation) aT = StdPrs_ToolShadedShape::Triangulation(aFace, aLoc);
		Handle(Poly_Triangulation) aT = BRep_Tool::Triangulation(aFace, aLoc);
		if (!aT.IsNull())
		{
			aNbTriangles += aT->NbTriangles();
			aNbVertices += aT->NbNodes();
			if (aNbTriangles >0 && aNbVertices > 3) break;
		}
	}
	if (aNbVertices < 3 || aNbTriangles <= 0)
	{
		return 0;
	}

	Standard_Real aUmin(0.0), aUmax(0.0), aVmin(0.0), aVmax(0.0), dUmax(0.0), dVmax(0.0);
	for (TopExp_Explorer aFaceIt(shape, TopAbs_FACE); aFaceIt.More(); aFaceIt.Next())
	{
		const TopoDS_Face& aFace = TopoDS::Face(aFaceIt.Current());
		//Handle(Poly_Triangulation) aT = StdPrs_ToolShadedShape::Triangulation(aFace, aLoc);
		Handle(Poly_Triangulation) aT = BRep_Tool::Triangulation(aFace, aLoc);
		if (aT.IsNull())
		{
			continue;
		}
		const gp_Trsf& aTrsf = aLoc.Transformation();

		// Determinant of transform matrix less then 0 means that mirror transform applied.
		Standard_Boolean isMirrored = aTrsf.VectorialPart().Determinant() < 0;

		// Extracts vertices & normals from nodes
		const TColgp_Array1OfPnt&   aNodes = aT->Nodes();


		const Standard_Integer aDecal = vPnt.size();
		
		{
		//	Poly_Connect aPolyConnect(aT);
			const TColgp_Array1OfPnt2d& aUVNodes = aT->UVNodes();


			BRepTools::UVBounds(aFace, aUmin, aUmax, aVmin, aVmax);
			dUmax = (aUmax - aUmin);
			dVmax = (aVmax - aVmin);

			for (Standard_Integer aNodeIter = aNodes.Lower(); aNodeIter <= aNodes.Upper(); ++aNodeIter)
			{
				aPoint = aNodes(aNodeIter);
				if (!aLoc.IsIdentity())
				{
					aPoint.Transform(aTrsf);

				}

				ZMPoint2D pntUvData = ZMPoint2D(aUVNodes(aNodeIter).X(), aUVNodes(aNodeIter).Y());
				const gp_Pnt2d aTexel = gp_Pnt2d((-theUVOrigin[0] + (theUVRepeat[0] * (pntUvData[0] - aUmin)) / dUmax) / theUVScale[0],
					(-theUVOrigin[1] + (theUVRepeat[1] * (pntUvData[1] - aVmin)) / dVmax) / theUVScale[1]);

				vPnt.push_back(ZMPoint3D(aPoint.X(), aPoint.Y(), aPoint.Z()));
				vUV.push_back(ZMPoint2D(aTexel.X(), aTexel.Y()));
			}

		}
		
		// Fill array with vertex and edge visibility info
		
		{
			const Poly_Array1OfTriangle& aTriangles = aT->Triangles();
			Standard_Integer anIndex[3];
			for (Standard_Integer aTriIter = 1; aTriIter <= aT->NbTriangles(); ++aTriIter)
			{
				if ((aFace.Orientation() == TopAbs_REVERSED) ^ isMirrored)
				{
					aTriangles(aTriIter).Get(anIndex[0], anIndex[2], anIndex[1]);
				}
				else
				{
					aTriangles(aTriIter).Get(anIndex[0], anIndex[1], anIndex[2]);
				}

				gp_Pnt aP1 = aNodes(anIndex[0]);
				gp_Pnt aP2 = aNodes(anIndex[1]);
				gp_Pnt aP3 = aNodes(anIndex[2]);

				gp_Vec aV1(aP1, aP2);
				if (aV1.SquareMagnitude() <= aPreci)
				{
					continue;
				}
				gp_Vec aV2(aP2, aP3);
				if (aV2.SquareMagnitude() <= aPreci)
				{
					continue;
				}
				gp_Vec aV3(aP3, aP1);
				if (aV3.SquareMagnitude() <= aPreci)
				{
					continue;
				}
				aV1.Normalize();
				aV2.Normalize();
				aV1.Cross(aV2);
				if (aV1.SquareMagnitude() > aPreci)
				{
					vTriIndex.push_back(anIndex[0] + aDecal -1);
					vTriIndex.push_back(anIndex[1] + aDecal - 1);
					vTriIndex.push_back(anIndex[2] + aDecal - 1);
				}
			}
		}
		
	}

	vNormal.resize(vPnt.size());
	vTangentData.resize(vPnt.size());
	for (int i = 0; i < vTriIndex.size(); i += 3)
	{
		int vInx[3] = { vTriIndex[i], vTriIndex[i + 1], vTriIndex[i + 2] };
		ZMPoint3D v[3] = {
			(vPnt[vInx[1]] - vPnt[vInx[0]]),
			(vPnt[vInx[2]] - vPnt[vInx[1]]),
			(vPnt[vInx[0]] - vPnt[vInx[2]])
		};
		for (int j = 0; j < 3; ++j)
		{
			vNormal[vInx[j]] += v[1].Crossed(v[0]);
			vTangentData[vInx[j]] += v[j];
			vTangentData[vInx[j]] += v[(j + 2) % 3];
		}

	}
	for (int i = 0; i < vTangentData.size(); ++i)
	{
		vNormal[i].Normalize();
		vTangentData[i].Normalize();
	}
	return 0;
}

void ScriptBase::TriangleShape(const TopoDS_Shape& shape, 
	ZMVector<ZMPoint3D>& vPnt,
	ZMVector<ZMPoint3D>& vNormal, 
	ZMVector<ZMPoint3D>& vTangentData,
	ZMVector<int>& vTriIndex
)
{
	BRepMesh_IncrementalMesh(shape, 1);
	for (TopExp_Explorer ex(shape, TopAbs_FACE); ex.More(); ex.Next())
	{
		ZMVector<ZMPoint3D> vPntTemp;
		ZMVector<ZMPoint3D> vNormalTemp;
		ZMVector<ZMPoint3D> vTangentNormalTemp;
		ZMVector<int> vNormalNum;
		ZMVector<int> vTriIndexTemp;
		const TopoDS_Face& F = TopoDS::Face(ex.Current());
		TopLoc_Location L;

		Handle(Poly_Triangulation) facing = BRep_Tool::Triangulation(F, L);
		if (facing.IsNull()) continue;
		vPntTemp.resize(facing->NbNodes());
	
		vTriIndexTemp.resize(facing->NbTriangles() * 3);
		for (Standard_Integer i = 1; i <= facing->NbNodes(); ++i)
		{
			gp_Pnt pnt = facing->Nodes().Value(i);
			vPntTemp[i - 1] = ZMPoint3D(pnt.X(), pnt.Y(), pnt.Z());
		}


		for (Standard_Integer i = 1; i <= facing->NbTriangles(); ++i)
		{
			if (F.Orientation() == TopAbs_REVERSED)
			{
				facing->Triangles().Value(i).Get(vTriIndexTemp[(i - 1) * 3 + 0], vTriIndexTemp[(i - 1) * 3 + 2], vTriIndexTemp[(i - 1) * 3 + 1]);
			}
			else
			{
				facing->Triangles().Value(i).Get(vTriIndexTemp[(i - 1) * 3 + 0], vTriIndexTemp[(i - 1) * 3 + 1], vTriIndexTemp[(i - 1) * 3 + 2]);
			}

			for (int j=0; j<3; ++j)
			{
				--vTriIndexTemp[(i - 1) * 3 + j];
			}
		}


		vNormalTemp.resize(vPntTemp.size());
		vTangentNormalTemp.resize(vPntTemp.size());
		vNormalNum.resize(vPntTemp.size(), 0);

		for (int i=0; i<vTriIndexTemp.size(); i+=3)
		{
			int vInx[3] = { vTriIndexTemp[i], vTriIndexTemp[i + 1], vTriIndexTemp[i + 2]  };
			ZMPoint3D v[3] = {
				(vPntTemp[vInx[1]] - vPntTemp[vInx[0]]),
				(vPntTemp[vInx[2]] - vPntTemp[vInx[1]]),
				(vPntTemp[vInx[0]] - vPntTemp[vInx[2]])
			};
			for (int j=0; j<3; ++j)
			{
				vNormalTemp[vInx[j]] += v[1].Crossed(v[0]);
				vTangentNormalTemp[vInx[j]] += v[j];
				vTangentNormalTemp[vInx[j]] += v[(j + 2)%3];
				vNormalNum[vInx[j]]++;
			}

		}
		for (int i=0; i<vNormalTemp.size(); ++i)
		{
			vNormalTemp[i].Normalize();
			vTangentNormalTemp[i].Normalize();
		}


		for (int& inx: vTriIndexTemp)
		{
			inx += vPnt.size();
		}

		vPnt.insert(vPnt.end(), vPntTemp.begin(), vPntTemp.end());
		
		vNormal.insert(vNormal.end(), vNormalTemp.begin(), vNormalTemp.end());

		vTangentData.insert(vTangentData.end(), vTangentNormalTemp.begin(), vTangentNormalTemp.end());
		
		vTriIndex.insert(vTriIndex.end(), vTriIndexTemp.begin(), vTriIndexTemp.end());
	}
}
